<?php
// +----------------------------------------------------------------------
// | 基础类型服务层
// +----------------------------------------------------------------------
// | 主要服务于任务相关数据操作
// +----------------------------------------------------------------------
// | 错误编码头 201xxx
// +----------------------------------------------------------------------

namespace common\service;

use common\exception\LogicException;
use common\model\EntityModel;
use common\model\ProjectModel;
use common\model\StatusModel;
use common\model\StepCategoryModel;
use common\model\StepModel;
use common\model\TaskModel;
use common\model\UserModel;
use support\ErrorCode;
use support\SingletonTrait;
use think\Exception;
use think\Hook;
use think\module\Module;
use Webman\Stomp\Client;


class TaskAssembleService
{
    use SingletonTrait;

    /**
     * 查询任务组装信息
     * @param int $id
     * @return array
     */
    public function getTaskAssembleInfo(int $id)
    {
        $res = [
            'project_name'       => '',
            'project_code'       => '',
            'project_type'       => '',
            'entity_type'        => '',
            'entity_id'          => '',
            'entity_code'        => '',
            'entity_name'        => '',
            'episode_name'       => '',
            'episode_code'       => '',
            'sequence_name'      => '',
            'sequence_code'      => '',
            'shot_name'          => '',
            'shot_code'          => '',
            'asset_type'         => '',
            'asset_type_name'    => '',
            'asset_name'         => '',
            'asset_code'         => '',
            'asset_grade'        => '',
            'level_name'         => '',
            'level_code'         => '',
            'level_grade'        => '',
            'status_name'        => '',
            'status_code'        => '',
            'status_color'       => '',
            'user_name'          => '',
            'user_icon'          => '',
            'step_name'          => '',
            'step_code'          => '',
            'step_category_code' => '',
            'step_category_name' => '',
            'design_type'        => '',
            'design_category'    => '',
            'design_grade'       => '',
            'id'                 => '',
            'man_hour'           => '',
            'actual_man_hour'    => '',
            'start_date'         => '',
            'due_date'           => '',
            'is_old_name'        => '',
            'relate_level_list'  => [],
            'relate_asset_list'  => [],
        ];

        $taskQueryFields = [
            'id',
            'project_id',
            'step_category_id',
            'step_id',
            'entity_id',
            'entity_module_id',
            'start_time',
            'man_hour',
            'actual_man_hour',
            'plan_end_time',
            'executor',
            'task_status',
            'is_old_name',
            'code',
            'json',
        ];

        $task         = model(TaskModel::class)->field(transform_custom_fields($taskQueryFields, 'task'))->find($id);
        $entity       = model(EntityModel::class)->field('id,name,code,initial_episode_code,module_id,entity_id,entity_module_id,showings_number,json')->find($task['entity_id']);
        $project      = model(ProjectModel::class)->field('name,code,work_dir,type')->find($task['project_id']);
        $step         = model(StepModel::class)->field('name,code')->find($task['step_id']);
        $stepCategory = model(StepCategoryModel::class)->field('name,code')->find($task['step_category_id']);
        $status       = model(StatusModel::class)->field('name,code,color')->find($task['task_status']);

        $episode      = model(EntityModel::class)
            ->where(['project_id' => $task['project_id'], 'module_id' => Module::$moduleDictData['module_index_by_code']['episode']['id'], 'code' => $entity['initial_episode_code']])
            ->field('name,code')->find();

        $user = [];

        if ($task['executor'] > 0) {
            $user = model(UserModel::class)->field('name,avatar')->find($task['executor']);
        }

        $sequence = [];
        if ($entity['module_id'] === Module::$moduleDictData['module_index_by_code']['shot']['id'] && $entity['entity_id'] > 0) {
            $sequence = model(EntityModel::class)->field('name,code')->find($entity['entity_id']);
        }

        $parentStepCode = [];
        if (!empty($task['json'])) {
            $taskJson = json_decode($task['json'], true);
            $parentStepCode = $taskJson['parent_step_code'] ?? null;
        }

        // task
        $res['id']               = $task['id'];
        $res['man_hour']         = $task['man_hour'];
        $res['actual_man_hour']  = $task['actual_man_hour'];
        $res['start_date']       = $task['start_time'];
        $res['due_date']         = $task['plan_end_time'];
        $res['is_old_name']      = $task['is_old_name'];
        $res['task_code']        = $task['code'];
        $res['parent_step_code'] = $parentStepCode;
        // project
        $res['project_code'] = $project['work_dir'];
        $res['project_name'] = $project['name'];
        $res['project_type'] = $project['type'];
        // entity
        $res['entity_type'] = Module::$moduleDictData['module_index_by_id'][$entity['module_id']]['code'] ?? '';
        $res['entity_id']   = $entity['id'];
        $res['entity_code'] = $entity['code'];
        $res['entity_name'] = $entity['name'];
        // any entity module
        $entityModuleCode = Module::$moduleDictData['module_index_by_id'][$entity['module_id']]['code'] ?? null;
        if ( ! empty($entityModuleCode)) {
            $res[$entityModuleCode . '_name']            = $res['entity_name'];
            $res[$entityModuleCode . '_code']            = $res['entity_code'];
            $res[$entityModuleCode . '_showings_number'] = $entity['showings_number'];
        }
        //  merge entity json attr
        $entityAttr = json_decode($entity['json'] ?? '{}', true);
        if (is_array($entityAttr)) {
            $res = $entityAttr + $res;
        }

        if (in_array($entity['module_id'], [module_by_code('shot')['id'], module_by_code('sequence')['id']])) {
            $levelIds = $entityAttr[$entityModuleCode . '_level'] ?? '';
            if (!empty($levelIds)) {
                $res['relate_level_list'] = model(EntityModel::class)->field('id,name,code')->where(['id' => ['IN', $levelIds]])->select();
            }
        }

        if (in_array($entity['module_id'], [module_by_code('shot')['id'], module_by_code('sequence')['id'], module_by_code('level')['id']])) {
            $assetIds = $entityAttr[$entityModuleCode . '_asset'] ?? '';
            if (!empty($assetIds)) {
                $res['relate_asset_list'] = model(EntityModel::class)->field('id,name,code')->where(['id' => ['IN', $assetIds]])->select();
            }
        }

        // episode
        if (!empty($episode)) {
            $res['episode_code'] = $episode['code'];
            $res['episode_name'] = $episode['name'];
        }
        // 针对序列 都给实体名称和code
        if ($entityModuleCode === 'sequence') {
            $res['sequence_name'] = $entity['name'];
            $res['sequence_code'] = $entity['code'];
        } else {
            // 其他环节 进行新老任务判断
            if ($task['is_old_name'] === 'yes') {
                // sequence
                if (!empty($sequence)) {
                    $res['sequence_name'] = $sequence['name'];
                    $res['sequence_code'] = $sequence['code'];
                } else if (empty($res['sequence_code'])) {
                    $res['sequence_code'] = $entity['showings_number'];
                }
            } else {
                $res['sequence_name'] = $res['sequence_code'] = $entity['showings_number'];
            }
        }
        // status
        if (!empty($status)) {
            $res['status_code'] = $status['code'];
            $res['status_name'] = $status['name'];
            $res['status_color'] = $status['color'];
        }
        // step
        if (!empty($step)) {
            $res['step_name'] = $step['name'];
            $res['step_code'] = $step['code'];
        }
        // stepCategory
        if (!empty($stepCategory)) {
            $res['step_category_name'] = $stepCategory['name'];
            $res['step_category_code'] = $stepCategory['code'];
        }
        // user
        if (!empty($user)) {
            $res['user_name'] = $user['name'];
            $res['user_icon'] = $user['avatar'];
        }

        return $res;
    }

    /**
     * 任务组装回调
     * @param int $taskId
     * @return int
     * @throws \think\Exception
     */
    public function taskAssembleCallback(int $taskId)
    {
        /**
         *
         * 组装回调
         *
         * 1. 通过任务查询出任务所属租户 通过任务租户id和当前用户租户id 来进行判断是外包组装完成还是内部组装完成
         * 2. 修改组装状态
         *  2.1. 内部组装完成就更新任务(task)的组装状态为已组装
         *  2.2 外部组装 就更新外部上游任务(order_task_upstream_version)的组装状态为已组装
         * 3. 循环检查下游任务是否就绪
         *
         */
        $res = 0;

        $task = model(TaskModel::class)->field('id,tenant_id,entity_id,step_id,json')->find($taskId);
        if (empty($task)) {
            throw new LogicException('task not found', ErrorCode::TASK_NOT_FOUND);
        }
        $statusService = StatusService::getInstance();
        $defaultStatus = $statusService->getTaskDefaultStatus();
        if (empty($defaultStatus)) {
            throw new LogicException('did not config default task start status', ErrorCode::MISSING_TASK_DEFAULT_START_STATUS_CONFIG);
        }
        $waitAssembleStatus = $statusService->getTaskDefaultWaitAssembleStatus();
        if (empty($waitAssembleStatus)) {
            throw new LogicException('did not config default task assemble status', ErrorCode::MISSING_STATUS);
        }

        // 未组装状态集合
        $notAssembleStatus = [
            $defaultStatus['id'],
            $waitAssembleStatus['id'],
        ];

        $taskListFilter = [
            'id' => $task['id'],
        ];
        $entity = model(EntityModel::class)->where(['id' => $task['entity_id']])->find();
        if ($entity['module_id'] === module_by_code('shot')['id']) {
            if ($entity['entity_id'] > 0) {
                $sequence = model(EntityModel::class)->where(['id' => $entity['entity_id']])->field("id,json->>'$.sequence_type' as sequence_type")->find();
                if (!empty($sequence['sequence_type']) && $sequence['sequence_type'] !== 'motion_capture') {
                    $childrenEntity = model(EntityModel::class)->where(['entity_id' => $entity['entity_id']])->field('id')->select();
                    if (!empty($childrenEntity)) {
                        $taskListFilter = [
                            'entity_id' => ['IN', array_column($childrenEntity, 'id')],
                            'step_type' => 'step',
                            'step_id' => $task['step_id'],
                        ];
                    }
                }
            }
        }

        $taskList = model(TaskModel::class)->where($taskListFilter)->field('id,name,code,JSON_UNQUOTE(JSON_EXTRACT(json,\'$.task_status\')) as task_status,is_assemble')->select();

        $taskReadyStatus = $statusService->getTaskDefaultReadyStatus();
        $updateAssembleList = [];
        $updateTaskStatusList = [];
        foreach ($taskList as $task) {
            if ($task['is_assemble'] === 'no') {
                $updateAssembleList[] = $task['id'];
            }
            if (in_array((int)$task['task_status'], $notAssembleStatus)) {
                $updateTaskStatusList[] = [
                    'id' => $task['id'],
                    'task_status' => $taskReadyStatus['id'],
                ];
            }
        }

        // 修改组装状态
        if (!empty($updateAssembleList)) {
            $res += model(TaskModel::class)->where(['id' => ['IN', $updateAssembleList]])->save(['is_assemble' => 'yes']);
        }

        // 修改任务状态
        if (!empty($updateTaskStatusList)) {
            CommonService::instance('task')->commonBatchUpdate($updateTaskStatusList, module_by_code('task'), false);
            $res += count($updateTaskStatusList);
        }

        return $res;
//
//        $downstreamTaskFields = [
//            'id', 'name', 'entity_id', 'step_id', 'step_category_id', 'executor', "json_unquote(json_extract(json,'$.task_status')) as task_status"
//        ];
//
//
//        if ($task['tenant_id'] === $tenantId) {
//            model(TaskModel::class)->where(['id' => $taskId])->save(['is_assemble' => 'yes']);
//            // 所有的下游实体
//            $entityIdList = $this->getDownstreamEntityList($task['entity_id'], false);
//            $entityIdList[] = $task['entity_id'];
//            $taskList = model(TaskModel::class)->where(['entity_id' => ['IN', implode(',', $entityIdList)], 'step_type' => 'step'])->field($downstreamTaskFields)->select();
//            $downstreamSteps = $this->stepService->getAllStepDownstreamMap();
//            $taskList = array_group_by($taskList, 'step_id');
//            $downstreamTaskList = [];
//            $this->findAllDownstreamTask($taskList, $downstreamSteps, $task, $downstreamTaskList);
//            // 状态筛选 未开始的任务
//        } else {
//            // 更新订单依赖文件的组装状态
//            $orderTaskUpstreamVersionList = model(OrderTaskUpstreamVersionModel::class)->where(['task_id' => $taskId, 'to_tenant_id' => $tenantId])->select();
//            if (count($orderTaskUpstreamVersionList) > 0) {
//                $orderTaskUpstreamVersionIdList = array_column($orderTaskUpstreamVersionList, 'id');
//                model(OrderTaskUpstreamVersionModel::class)->where(['id' => ['IN', $orderTaskUpstreamVersionIdList],])->save(['is_assemble' => 'yes']);
//            }
//            // 查询出依赖我的所有任务 订单状态为进行中
//            $orderTaskFileListList = model(OrderTaskFileListModel::class)
//                ->alias('otfl')
//                ->join('`order` o ON o.id = otfl.order_id')
//                ->where([
//                    'o.status' => 'inprogress',
//                    'otfl.from_tenant_id' => $task['tenant_id'],
//                    'otfl.to_tenant_id' => $tenantId,
//                    'otfl.task_id' => $taskId,
//                ])->select();
//            if (count($orderTaskFileListList) === 0) {
//                return;
//            }
//            $downstreamTaskIdList = array_column($orderTaskFileListList, 'downstream_task_id', 'downstream_task_id');
//            $downstreamTaskList = model(TaskModel::class)->where(['id' => ['IN', $downstreamTaskIdList]])->field($downstreamTaskFields)->select();
//        }
//        $downstreamTaskList = array_filter($downstreamTaskList, function ($item) use ($defaultStatus) {
//            return $item['task_status'] == $defaultStatus['id'];
//        });
//        if (count($downstreamTaskList) === 0) {
//            return;
//        }
//        $downstreamTaskIdList = array_column($downstreamTaskList, 'id', 'id');
//        // 下游任务循环检查 是否所有的上游已经组装
//        foreach ($downstreamTaskIdList as $downstreamTaskId) {
//            Client::send('task_ready_check', json_encode(['task_id' => $downstreamTaskId]));
//        }
    }

    /**
     * 组装通知用的任务名称
     * @param $entity
     * @param $task
     * @return string
     */
    public function getNotifyTaskName($entity, $task)
    {
        if (empty($entity)) {
            return '【' . $task['name'] . '】';
        }
        return '【' . $entity['initial_episode_code'] . '/' . $entity['showings_number'] . '/' . $entity['name'] . '/' . $task['name'] . '】';
    }

    /**
     * 检查下游任务待组装状态
     * 遍历下游任务 检查是否该任务的所有上游任务 是否已完成 ,
     *      如已完成 进待组装状态
     * @param $id
     * @throws Exception
     */
    public function checkDownstreamWaitAssembleStatus($id)
    {
        $currentTask = model(TaskModel::class)->find($id);
        $currentEntity = model(EntityModel::class)
            ->field('id,initial_episode_code,showings_number,name')
            ->find($currentTask['entity_id']);

        $currentTaskName = $this->getNotifyTaskName($currentEntity, $currentTask);
        $downstreamTaskList = TaskUpstreamService::getInstance()->getDownstreamTaskList($id);

        $templateId = OptionsService::getInstance()->getTaskRemindCommonTemplateId();

        $entityIdList = array_column($downstreamTaskList, 'entity_id', 'entity_id');
        $entityList = model(EntityModel::class)->where(['id' => ['IN', implode(',', $entityIdList)]])
            ->field('id,initial_episode_code,showings_number,name')
            ->select();
        $entityList = array_column($entityList, null, 'id');
        $notStartStatus = StatusService::getInstance()->getTaskDefaultStatus();
        $waitAssembleStatus = StatusService::getInstance()->getTaskWaitAssembleStatus();
        $checkStatus = [$waitAssembleStatus['id'], $notStartStatus['id']];
        $commonService = CommonService::instance('task');
        $commonService->setCurrentModuleCode("task");
        foreach ($downstreamTaskList as $task) {
            if ($task['entity_id'] == $currentEntity['id']) {
                $relateType = '上游';
            } else {
                $relateType = '关联';
            }
            $entity = $entityList[$task['entity_id']] ?? null;
            if (empty($entity)) {
                continue;
            }
            // 1. 外包任务 替换为外包任务id
            if (in_array($task['executor_type'], TaskModel::OUTSIDE_EXECUTE_TYPE_GROUP)) {
                $task = OrderService::getInstance()->getOutsideTaskByTaskId($task['id']);
                if (empty($task)) {
                    continue;
                }
            }
            // 2. 检查任务上游的完成状态/下载状态
            $checkResult = TaskUpstreamService::getInstance()->getUpstreamTaskDoneStatus($task);
            $taskJson = json_decode($task['json'], true);
            $currentTaskStatus = $taskJson['task_status'] ?? null;

            // 3. 修改任务的组装状态
            if (in_array($currentTaskStatus, $checkStatus)) {
                if (!$checkResult['flag']) {
                    // 不能组装 进未开始状态
                    if ($currentTaskStatus != $notStartStatus['id']) {
                        model(TaskModel::class)->modifyItem($commonService->dealUpdateCustomField([
                            'id' => $task['id'],
                            'task_status' => $notStartStatus['id'],
                        ], 'task'));
                    }
                } else {
                    // 可以组装 进待组装状态
                    if ($currentTaskStatus != $waitAssembleStatus['id']) {
                        model(TaskModel::class)->modifyItem($commonService->dealUpdateCustomField([
                            'id' => $task['id'],
                            'task_status' => $waitAssembleStatus['id'],
                        ], 'task'));
                    }
                }
            }

            // 4. 根据检查结果  小助手通知用户任务的上下游情况
            $hookData = [
                'type' => 'task_upstream_done',
                'data' => [
                    'task' => $task,
                    'entity' => $entity,
                    'check_result' => $checkResult,
                    'current_task_name' => $currentTaskName,
                    'relate_type' => $relateType,
                    'template_id' => $templateId,
                ],
            ];
            Hook::listen('task_done_after', $hookData);
        }
    }

    /**
     * 检查任务能否组装 能组装修改为组装状态
     * @param int $taskId
     * @return array
     * @throws Exception
     */
    public function checkTaskCanAssemble(int $taskId)
    {
        $res = [
            'flag' => false,
            'msg' => '',
        ];
        /**
         * 当前需要检测的任务状态
         * 1. 未开始
         * 2. 待组装
         */

        /**
         * 检查上游任务是否完成/下载
         * 1. 查询出所有的上游任务
         * 2. 分离 本租户的上游任务 和 已外包/外部的上游任务
         * 3. 查询 已外包/外部的上游任务 是否
         *
         */
        $task = model(TaskModel::class)->find($taskId);

        if (in_array($task['executor_type'], TaskModel::OUTSIDE_EXECUTE_TYPE_GROUP)) {
            // 任务已经被外包出去 检查外包的任务能否组装
            $outsideTask = OrderService::getInstance()->getOutsideTaskByTaskId($task['id']);
            if (!empty($outsideTask)) {
                $task = $outsideTask;
//            } else {
//                throw new LogicException('outside task not found', ErrorCode::TASK_NOT_FOUND);
            }
        }
        $currentTaskJsonFields = json_decode($task['json'], true);
        $readyStatus = StatusService::getInstance()->getTaskDefaultReadyStatus();
        $notStartStatus = StatusService::getInstance()->getTaskDefaultStatus();
        $waitAssembleStatus = StatusService::getInstance()->getTaskWaitAssembleStatus();
        $closedStatus = StatusService::getInstance()->getTaskDefaultClosedStatus();

        if (empty($waitAssembleStatus)) {
            throw new LogicException("status [wait_assemble] not found", ErrorCode::MISSING_STATUS);
        }
        if ($currentTaskJsonFields['task_status'] == $closedStatus['id']) {
            // 任务已取消
            throw new LogicException("task closed", ErrorCode::TASK_CLOSED);
        }
        // 如果是概念任务,并且已就绪
        if ($task['entity_module_id'] == module_by_code('design')['id'] && $currentTaskJsonFields['task_status'] == $readyStatus['id']) {
            $res['msg'] = '当前概念任务已就绪,不需要组装';
            $res['flag'] = true;
            return $res;
        }
        $checkStatus = [$waitAssembleStatus['id'], $notStartStatus['id']];
        if (!in_array($currentTaskJsonFields['task_status'], $checkStatus)) {
            $res['msg'] = '当前任务状态不需要检查组装状态';
            return $res;
        }
        // 是否为默认状态
        // 保证概念历史数据 能够走通新流程
        if ($task['entity_module_id'] == module_by_code('design')['id']) {
            if ($currentTaskJsonFields['task_status'] == $notStartStatus['id']) {
                $taskUpdateData = [
                    'id' => $task['id'],
                    'task_status' => $readyStatus['id'],
                ];
                CommonService::instance('task')->update($taskUpdateData, 'task', false);
                $res['msg'] = '概念任务不需要组装,进入已就绪';
                $res['flag'] = true;
                return $res;
            }
            Client::send('task_ready_check', build_queue_data_with_xu(['task_id' => $task['id']]));
            $res['msg'] = '概念任务不需要组装';
            return $res;
        } elseif (in_array($task['code'], ['efx_asset', 'creatives_asset'])) {
            if ($currentTaskJsonFields['task_status'] == $notStartStatus['id']) {
                $taskUpdateData = [
                    'id' => $taskId,
                    'task_status' => $readyStatus['id'],
                ];
                CommonService::instance('task')->update($taskUpdateData, 'task', false);
                $res['msg'] = '特效资产和创意资产任务不需要组装,进入已就绪';
                $res['flag'] = true;
                return $res;
            }
            Client::send('task_ready_check', build_queue_data_with_xu(['task_id' => $task['id']]));
            $res['msg'] = '特效资产和创意资产任务不需要组装';
            return $res;
        }

        $upstreamDoneStatus = TaskUpstreamService::getInstance()->getUpstreamTaskDoneStatus($task);
        $res = array_merge($res, $upstreamDoneStatus);
        $commonService = CommonService::instance('task');
        $commonService->setCurrentModuleCode("task");
        APP_DEBUG && trace(var_export(['res' => $res, 'current_task_status' => $currentTaskJsonFields['task_status']], true), 'DEBUG');
        if (!$res['flag']) {
            // 不能组装 进未开始状态
            if ($currentTaskJsonFields['task_status'] != $notStartStatus['id']) {
                model(TaskModel::class)->modifyItem($commonService->dealUpdateCustomField([
                    'id' => $task['id'],
                    'task_status' => $notStartStatus['id'],
                ], 'task'));
            }
        } else {
            // 可以组装 进待组装状态
            if ($currentTaskJsonFields['task_status'] != $waitAssembleStatus['id']) {
                model(TaskModel::class)->modifyItem($commonService->dealUpdateCustomField([
                    'id' => $task['id'],
                    'task_status' => $waitAssembleStatus['id'],
                ], 'task'));
            }
        }
        $res['is_skip_upstream'] = StepProjectService::getInstance()->getIsSkipUpstream($task['step_id'], $task['project_id']); // 当前任务是否能跳过上游组装限制判断
        return $res;
    }
}
